package com.shire.shire_interface.service.commandcenter;

import com.shire.shire_interface.constants.WxConstants;
import com.shire.shire_interface.constants.WxConstants.CmdType;
import com.shire.shire_interface.model.command.WxCommand;
import com.shire.shire_interface.model.command.WxCommandExample;
import com.shire.shire_interface.model.user.WxAccount;
import com.shire.shire_interface.service.commandcenter.queues.*;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
public class CSCommandStore {
    private WxAccount csAccount;

    @Resource
    private WxCommandService commandService;

    private Queue<WxCommand> instantCommands;
    private Queue<WxCommand> waitList;
    private Map<Long, WxCommand> sentMap;

    private TimelineCommandQueue timelineCommandQueue;

    private JoinGroupCommandQueue joinGroupCommandQueue;

    private InviteCommandQueue inviteCommandQueue;

    private MassMessageCommandQueue massMessageCommandQueue;

    private ValidationCommandQueue validationCommandQueue;

    @Resource
    private TimelineCommandQueue timelineCommandQueueInst;
    @Resource
    private JoinGroupCommandQueue joinGroupCommandQueueInst;
    @Resource
    private InviteCommandQueue inviteCommandQueueInst;
    @Resource
    private MassMessageCommandQueue massMessageCommandQueueInst;
    @Resource
    private ValidationCommandQueue validationCommandQueueInst;

    private static CSCommandStore instance;
    private ExecutorService executorService;

    @PostConstruct
    public void init() {
        instance = this;
    }

    public static CSCommandStore getInstance() {
        return instance;
    }

    public CSCommandStore getCommandStoreForAccount(WxAccount account) {
        return new CSCommandStore(account, commandService);
    }

    public CSCommandStore() {
        super();
    }

    public CSCommandStore(WxAccount csAccount, WxCommandService commandService) {
        this.csAccount = csAccount;
        this.commandService = commandService;
        this.instantCommands = new LinkedList<WxCommand>();
        this.sentMap = new HashMap<>();
        this.waitList = new LinkedList<>();
        executorService = new ThreadPoolExecutor(1,2,100, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(4), new ThreadPoolExecutor.DiscardOldestPolicy());

        loadCommands();
    }

    public void loadCommands() {
        loadInstantCommands();

        this.validationCommandQueue = ValidationCommandQueue.getInstance().getQueueForAccount(csAccount);
        this.timelineCommandQueue = TimelineCommandQueue.getInstance().getQueueForAccount(csAccount);
        this.joinGroupCommandQueue = JoinGroupCommandQueue.getInstance().getQueueForAccount(csAccount);
        this.inviteCommandQueue = InviteCommandQueue.getInstance().getQueueForAccount(csAccount);
        this.massMessageCommandQueue = MassMessageCommandQueue.getInstance().getQueueForAccount(csAccount);
    }

    /**
     * 批量加载待发送指令
     */
    private void loadInstantCommands() {
        WxCommandExample example = new WxCommandExample();
        List<Integer> chatCmdTypeList = new ArrayList<>();
        chatCmdTypeList.add(CmdType.CRK_SEND_LINK.getCode());
        chatCmdTypeList.add(CmdType.CRK_SEND_CARD.getCode());
        chatCmdTypeList.add(CmdType.CRK_SEND_CHAT_D_VIDEO.getCode());
        chatCmdTypeList.add(CmdType.CRK_SEND_CHAT_D_NAMECARD.getCode());
        chatCmdTypeList.add(CmdType.CRK_SEND_CHAT_D_LINK.getCode());
        chatCmdTypeList.add(CmdType.CRK_SEND_CHAT_D_IMAGE.getCode());
        chatCmdTypeList.add(CmdType.CRK_SEND_CHAT_D_FILE.getCode());
        chatCmdTypeList.add(CmdType.CRK_SEND_CHAT_D.getCode());


        List<Integer> statusList = new ArrayList<>();
        statusList.add(WxConstants.CmdStatus.SENT.getCode());
        statusList.add(WxConstants.CmdStatus.NEW.getCode());

//        WxCommandExample sentExample = new WxCommandExample();
//        sentExample.createCriteria().andAccountidEqualTo(csAccount.getId()).andCmdstatusEqualTo(WxConstants.CmdStatus.SENT.getCode());
//        List<WxCommand> sendCmdList = commandService.getCommandDao().selectByExampleWithBLOBs(sentExample);
//        for(WxCommand cmd : sendCmdList){
//            this.sentMap.put(cmd.getId(),cmd);
//        }

        Date limitDate = new Date((new Date()).getTime() - 1000*60*60*24);
        example.createCriteria().andAccountidEqualTo(csAccount.getId()).andCmdstatusIn(statusList).andStarttimeGreaterThan(limitDate);
        List<WxCommand> commands = commandService.getCommandDao().selectByExampleWithBLOBs(example);
        this.instantCommands.addAll(commands);

//        WxCommandExample waitExample = new WxCommandExample();
//        waitExample.createCriteria().andAccountidEqualTo(csAccount.getId()).andCmdstatusEqualTo(WxConstants.CmdStatus.NEW.getCode()).andTasktypeIsNotNull().andCmdtypeIn(chatCmdTypeList);
//        waitExample.or().andAccountidEqualTo(csAccount.getId()).andCmdstatusEqualTo(WxConstants.CmdStatus.NEW.getCode()).andCmdtypeNotIn(chatCmdTypeList);
//        this.waitList.addAll(commandService.getCommandDao().selectByExample(waitExample));
    }

    public synchronized WxCommand getNextCommand() {

        //即时命令队列优先返回
        if (this.instantCommands.peek() != null) {
            WxCommand command = udpateCommandBeforeSend(this.instantCommands.poll());
            this.sentMap.put(command.getId(),command);
            System.out.println("++++++++====== cmd store cmd id:"+command.getId());
            return command;
        }


        if (this.waitList.peek() != null) {
            WxCommand cmd = udpateCommandBeforeSend(this.waitList.poll());
            this.sentMap.put(cmd.getId(),cmd);
            return cmd;
        }


        Thread tempThread = new Thread() {
            @Override
            public void run() {
                //优先级好像没啥用
                try {
                    WxCommand command = null;
                    List<WxCommand> batchCmd = massMessageCommandQueue.getNextBatch();
                    if (batchCmd.size() > 0) {
                        System.out.println("++++++++>>>>  产生批量指令："+ batchCmd.size() +"<<<<++++++");
                        instantCommands.addAll(batchCmd);

                    }
                    command = inviteCommandQueue.getNextCommand();
                    if (command != null) {
                        instantCommands.add(command);

                    }
                    command = timelineCommandQueue.getNextCommand();
                    if (command != null) {
                        instantCommands.add(command);
                    }
                    command = joinGroupCommandQueue.getNextCommand();
                    if (command != null) {
                        instantCommands.add(command);
                    }
                    command = validationCommandQueue.getNextCommand();
                    if (command != null) {
                        instantCommands.add(command);
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        };
        executorService.submit(tempThread);

        return null;
    }

    public void responseCommand(WxCommand cmd){
        System.out.println("++++++++====== response store cmd id:"+cmd.getId());
        if (this.sentMap.containsKey(cmd.getId())){
            this.sentMap.remove(cmd.getId());
            if (cmd.getCmdstatus() == WxConstants.CmdStatus.NEW.getCode()){
                this.instantCommands.add(cmd);
            }
        }
    }

    private WxCommand udpateCommandBeforeSend(WxCommand cmd) {
        Date dt = new Date();
        cmd.setCmdstatus(WxConstants.CmdStatus.SENT.getCode());
        cmd.setSenttime(dt);
        cmd.setStarttime(dt);
        cmd.setUpdatetime(dt);
        commandService.updateWxCommand(cmd);
        return cmd;
    }

    public WxAccount getCsAccount() {
        return csAccount;
    }

    public void setCsAccount(WxAccount csAccount) {
        this.csAccount = csAccount;
    }

    public void addCommand(WxCommand wxCommand) {
//        if (wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CHAT_D.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CHAT.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CHAT_D_FILE.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CHAT_D_IMAGE.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CHAT_D_LINK.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CHAT_D_NAMECARD.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CHAT_D_VIDEO.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_CARD.getCode()
//                || wxCommand.getCmdtype() == WxConstants.CmdType.CRK_SEND_LINK.getCode()) {
            this.instantCommands.add(wxCommand);
//        } else {
//            this.waitList.add(wxCommand);
//        }
    }

    synchronized List<WxCommand>getOvertimeCommand() {
        List<WxCommand> overtimeCmdlist = new ArrayList<>();
        Long curTime = (new Date()).getTime();

        Iterator<WxCommand> commandsIter = this.instantCommands.iterator();
        while (commandsIter.hasNext()){
            WxCommand command = commandsIter.next();
            if (curTime - command.getStarttime().getTime() > 10 * 60 * 1000) {
                overtimeCmdlist.add(command);
                commandsIter.remove();
            }
        }


        Iterator<Map.Entry<Long,WxCommand>> it = sentMap.entrySet().iterator();

        while (it.hasNext()){
            Map.Entry<Long,WxCommand> entry = it.next();
            if (curTime - entry.getValue().getStarttime().getTime() > 30 * 60 *1000){
                overtimeCmdlist.add(entry.getValue());
                it.remove();
            }
        }
        return overtimeCmdlist;
    }

    synchronized public void retryCommands(){
        WxCommandExample example = new WxCommandExample();

        List<Integer> statusList = new ArrayList<>();
        statusList.add(WxConstants.CmdStatus.SENT.getCode());
        statusList.add(WxConstants.CmdStatus.NEW.getCode());

        Date retryDate = new Date((new Date()).getTime() - 1000*60*5);

        Date limitDate = new Date((new Date()).getTime() - 1000*60*50);
        example.createCriteria().andTrytimesLessThan(3).andAccountidEqualTo(csAccount.getId()).andCmdstatusIn(statusList).andSenttimeLessThan(retryDate).andSenttimeGreaterThan(limitDate);
        example.or().andTrytimesLessThan(3).andAccountidEqualTo(csAccount.getId()).andCmdstatusIn(statusList).andSenttimeIsNull();
        List<WxCommand> commands = commandService.getCommandDao().selectByExampleWithBLOBs(example);

        for (WxCommand cmd : commands) {
            cmd.setTrytimes(cmd.getTrytimes()+1);

            this.instantCommands.add(cmd);
        }
    }
}
